import time
import uuid
import sha
import logging

from xml.dom.minidom import parseString
from xml.parsers.expat import ExpatError
from datetime import datetime, timedelta

from google.appengine.ext.webapp import template
from google.appengine.api import urlfetch
from google.appengine.ext import db

from views.base import ViewBase
from models import Server, Room, Player

class GameServer(ViewBase):
    def register(self, server_data, format='old'):
        if not server_data['cookie'] or server_data['id'] == "":
            return self.error("REGISTER ERROR: This Meta requires an ID and cookie to be set. Use 0 for a new server.")

        logging.info("Logging Server: " + server_data['id'] + "\n" + server_data['name'])
        if int(server_data['id']) == 0:
            self.new_server(server_data)
        else:
            self.update_server(server_data)

        return template.render(self.get_template('meta/register.xml'), self.context)

    def unregister(self, server_data, format='old'):
        if not server_data['cookie'] and server_data['id'] == "":
            return self.error("UNREGISTER ERROR: This Meta requires an ID and cookie to be set. Use 0 for a new server.")

        s = Server.gql("WHERE id = :id AND cookie = :cookie", id=server_data['id'], cookie=server_data['cookie']).get()
        if s:
            self._drop_rooms(s)
            s.delete()

        return template.render(self.get_template('meta/unregister.xml'), self.context)

    def register_rooms(self, room_data, format='old'):
        s = Server.gql("WHERE id = :id", id=room_data['server_id']).get()
        self._drop_rooms(s)

        if format == 'old':
            return
        """
            room_data['rooms'] = room_data.get('rooms', {})

            for key, value in room_data.iteritems():
                if key.startswith('rooms]'):
                    attr = key.split("][")
                    room_data['rooms'][attr[1]] = room_data['rooms'].get(attr[1], {})

                    if attr[2] == 'players':
                        room_data['rooms'][attr[1]][attr[2]] = room_data['rooms'][attr[1]].get(attr[2], {})
                        room_data['rooms'][attr[1]][attr[2]][attr[3]] = value
                    else:
                        room_data['rooms'][attr[1]][attr[2]] = value
        """

        for rid, data in room_data['rooms'].iteritems():
            data['players'] = data.get('players', {})

            r = Room(id=int(rid),
                    name=self.esscaped(data['name']),
                    pwd=data['pwd'][0].upper().startswith('T'),
                    num_users=len(data['players']),
                    server=s)
            r.put()

            for name in data['players'].itervalues():
                p = Player(name=self.esscaped(name), room=r)
                p.put()


        return template.render(self.get_template('meta/metas.xml'), self.context)


    def list(self, server_data, format='old'):
        self.context['servers'] = Server.all()

        return template.render(self.get_template('meta/list.xml'), self.context)

    def error(self, msg):
        self.context['err_msg'] = msg

        return template.render(self.get_template('meta/error.xml'), self.context)

    def ping(self, data, format='old'):
        self.context['timestamp'] = int(time.mktime(datetime.now().timetuple()))

        return template.render(self.get_template('meta/ping.xml'), self.context)

    def cleanup(self):
        if int(time.time()) % 10:
            return

        q = Server.gql("WHERE expiration < :1", datetime.now())
        old = q.fetch(q.count())

        for s in old:
            self._drop_rooms(s)
        db.delete(old)

    def new_server(self, server_data):
        self.context['server'] = Server.gql("WHERE address = :address AND port = :port",
                                            address=server_data['address'],
                                            port=int(server_data['port']))

        enc_str = '%s:%s/%s' % (server_data['address'], server_data['port'], server_data['name'])
        logging.info(enc_str)
        tmp = sha.new()
        tmp.update(enc_str)

        cookie = str(uuid.uuid4())
        experation = datetime.now() + timedelta(seconds=720)

        if self.context['server'].count() == 0:
            self.context['server'] = Server()
        else:
            self.context['server'] = self.context['server'].get()

        self.context['server'].id = str(int(tmp.hexdigest()[-12:], 16))
        self.context['server'].name = self.esscaped(server_data['name'])
        self.context['server'].address = server_data['address']
        self.context['server'].port = int(server_data['port'])
        self.context['server'].expiration = experation
        self.context['server'].version = server_data['version']
        self.context['server'].cookie = cookie
        self.context['server'].start = datetime.now()
        self.context['server'].num_users = int(server_data['num_users'])

        self.context['server'].put()

    def update_server(self, server_data):
        self.context['server'] = Server.gql("WHERE id = :id AND cookie = :cookie", id=server_data['id'], cookie=server_data['cookie'])

        if self.context['server'].count() == 0:
            self.new_server(server_data)
        else:
            self.context['server'] = self.context['server'].get()

        logging.info("Updating: " + server_data['name'] + " == " +  self.context['server'].name +"\nSetting Users to: " +  str(server_data['num_users']))

        self.context['server'].expiration = datetime.now() + timedelta(seconds=720)
        self.context['server'].num_users = int(server_data['num_users'])
        self.context['server'].name = self.esscaped(server_data['name'])

        self.context['server'].put()


    def _drop_rooms(self, server):
        try:
            for r in server.rooms:
                self._drop_players(r)
                #r.delete()
            db.delete(server.rooms)
        except AttributeError, e:
            pass
        except IndexError:
            pass

    def _drop_players(self, room):
        try:
            db.delete(room.players)
        except AttributeError, e:
            pass